//
// Created by konglinglong on 2020/9/6.
//

#include "../include/l1Api.h"
#include "../../lcore/misc/misc.h"
#include "l1Ul/l1Ul.h"
#include "l1Dl/l1Dl.h"

S_RanL1Context *gpRanL1Context = NULL;

int32_t RAN_L1_LoadCellConfig(S_RanL1CellInst *pCellInst, S_RanCellContext *pCellContext)
{
    pCellInst->inUse = pCellContext->inUse;

    pCellInst->dlSubcarrierSpacing = pCellContext->dlSubcarrierSpacing;
    pCellInst->slotNum = gNormalCpScs2SlotNumTable[pCellContext->dlSubcarrierSpacing];

    pCellInst->pL2L1CpMsgQue = pCellContext->pL2L1CpMsgQue;
    pCellInst->pL1L2CpMsgQue = pCellContext->pL1L2CpMsgQue;
    pCellInst->pL2L1UpMsgQue = pCellContext->pL2L1UpMsgQue;
    pCellInst->pL1L2UpMsgQue = pCellContext->pL1L2UpMsgQue;

    return O5G_RC_OK;
}

int32_t RAN_L1_LoadNodeBConfig(S_RanL1NbInst *pNbInst, S_RanNodeBContext *pNbContext)
{
    int32_t rc;
    uint32_t loop;

    pNbInst->nbId = pNbContext->nbId;
    pNbInst->inUse = pNbContext->inUse;

    pNbInst->cellNum = pNbContext->cellNum;
    for (loop = 0; loop < pNbInst->cellNum; loop++)
    {
        rc = RAN_L1_LoadCellConfig(&pNbInst->cellInst[loop], &pNbContext->cellContext[loop]);
        IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_L1_LoadNodeBConfig() RAN_L1_LoadCellConfig() error!\n");
    }

    return O5G_RC_OK;
}

int32_t RAN_L1_LoadConfig()
{
    int32_t rc;
    uint32_t loop;

    gpRanL1Context->nbNum = gpRanContext->nbNum;
    for (loop = 0; loop < gpRanL1Context->nbNum; loop++)
    {
        rc = RAN_L1_LoadNodeBConfig(&gpRanL1Context->nbInst[loop], &gpRanContext->nbContext[loop]);
        IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_L1_LoadConfig() RAN_L1_LoadNodeBConfig() error!\n");
    }

    return O5G_RC_OK;
}

int32_t RAN_L1_Init(void)
{
    int32_t rc;
    uint32_t nbLoop;
    uint32_t cellLoop;
    uint32_t ttiBuffLoop;
    S_RanL1NbInst *pNbInst;
    S_RanL1CellInst *pCellInst;
    char ttiIntPipeName[256];

    gpRanL1Context = OS_MemCalloc(1, sizeof(S_RanL1Context));
    IF_RETURN(==, NULL, gpRanL1Context, O5G_RC_ER, "RAN_L1_Init() OS_MemCalloc() error!\n");

    rc = RAN_L1_LoadConfig();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_L1_Init() RAN_L1_LoadConfig() error!\n");

    RAN_INIT_INST_IDX(gpRanL1Context);

    for (nbLoop = 0; nbLoop < RAN_NODEB_NUM_MAX; nbLoop++)
    {
        pNbInst = &gpRanL1Context->nbInst[nbLoop];
        IF_CONTINUE(==, FALSE, pNbInst->inUse, NO_LOG_MESSAGE);
        for (cellLoop = 0; cellLoop < RAN_CELL_NUM_MAX; cellLoop++)
        {
            pCellInst = &pNbInst->cellInst[cellLoop];
            IF_CONTINUE(==, FALSE, pCellInst->inUse, NO_LOG_MESSAGE);
            snprintf(ttiIntPipeName, sizeof(ttiIntPipeName), "%s_nb%u_cell%u", RAN_TTI_INT_PIPE_NAME, nbLoop, cellLoop);
            rc = OS_SelectInit(&pCellInst->ttiIntSelector, ttiIntPipeName);
            IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_L1_Init() OS_SelectInit(ttiIntSelector) error!\n");
            CSP_LOG_INF(RAN_MD_L1_COMM, "RAN_L1_Init() nb[%u] cell[%u] TTI_INT_PIPE[%s].\n", nbLoop, cellLoop, (char *)ttiIntPipeName);

            pCellInst->pRanUeQue = VarRB_Create("RanUeQueShm", 4 * 1024 * 1024, VAR_RB_TYPE_SPSC);
            IF_RETURN(==, NULL, pCellInst->pRanUeQue, O5G_RC_ER, "RAN_L1_Init() VarRB_Create(pRanUeQue) error!\n");
            pCellInst->pUeRanQue = VarRB_Create("UeRanQueShm", 4 * 1024 * 1024, VAR_RB_TYPE_SPSC);
            IF_RETURN(==, NULL, pCellInst->pUeRanQue, O5G_RC_ER, "RAN_L1_Init() VarRB_Create(pUeRanQue) error!\n");

            for (ttiBuffLoop = 0; ttiBuffLoop < RAN_L1_TTI_BUFF_NUM; ttiBuffLoop++)
            {
                pCellInst->pUlTtiBuff[ttiBuffLoop] = VarRB_Create(NULL, 4 * 1024 * 1024, VAR_RB_TYPE_MPSC);
                IF_RETURN(==, NULL, pCellInst->pUlTtiBuff[ttiBuffLoop], O5G_RC_ER, "RAN_L1_Init() VarRB_Create() error!\n");
                pCellInst->pDlTtiBuff[ttiBuffLoop] = VarRB_Create(NULL, 4 * 1024 * 1024, VAR_RB_TYPE_MPSC);
                IF_RETURN(==, NULL, pCellInst->pDlTtiBuff[ttiBuffLoop], O5G_RC_ER, "RAN_L1_Init() VarRB_Create() error!\n");
            }
        }
    }

    CSP_LOG_INF(RAN_MD_L1_COMM, "RAN_L1_Init() success.\n");

    return O5G_RC_OK;
}

int32_t RAN_L1_Final(void)
{
    int32_t rc;
    uint32_t nbLoop;
    uint32_t cellLoop;
    uint32_t ttiBuffLoop;
    S_RanL1NbInst *pNbInst;
    S_RanL1CellInst *pCellInst;

    for (nbLoop = 0; nbLoop < RAN_NODEB_NUM_MAX; nbLoop++)
    {
        pNbInst = &gpRanL1Context->nbInst[nbLoop];
        IF_CONTINUE(==, FALSE, pNbInst->inUse, NO_LOG_MESSAGE);
        for (cellLoop = 0; cellLoop < RAN_CELL_NUM_MAX; cellLoop++)
        {
            pCellInst = &pNbInst->cellInst[cellLoop];
            IF_CONTINUE(==, FALSE, pCellInst->inUse, NO_LOG_MESSAGE);
            rc = OS_SelectFinal(&pCellInst->ttiIntSelector);
            IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_L1_Final() OS_SelectFinal(ttiIntSelector) error!\n");

            VarRB_Destroy(pCellInst->pRanUeQue);
            VarRB_Destroy(pCellInst->pUeRanQue);

            for (ttiBuffLoop = 0; ttiBuffLoop < RAN_L1_TTI_BUFF_NUM; ttiBuffLoop++)
            {
                VarRB_Destroy(pCellInst->pUlTtiBuff[ttiBuffLoop]);
                VarRB_Destroy(pCellInst->pDlTtiBuff[ttiBuffLoop]);
            }
        }
    }

    OS_MemFree(gpRanL1Context);

    CSP_LOG_INF(RAN_MD_L1_COMM, "RAN_L1_Final() success.\n");

    return O5G_RC_OK;
}

void RAN_L1_CellProcess(S_RanL1CellInst *pCellInst)
{
    int32_t rc;
    uint32_t size;
    S_RanCommMsgHdr *pCommMsgHdr;

    while (1)
    {
        pCommMsgHdr = VarRB_ReadRequest(pCellInst->pL2L1CpMsgQue, &size);
        IF_BREAK(==, NULL, pCommMsgHdr, NO_LOG_MESSAGE);

        switch (pCommMsgHdr->type)
        {
        case RAN_L1_API_MSG_PARAMS_REQ:
        {
            CSP_LOG_INF(RAN_MD_L1_COMM, "RAN_L1_CellCpProcess() recv RAN_L1_API_MSG_PARAMS_REQ\n");

            S_RanL1ApiMsgParamsRsp paramsRsp;

            paramsRsp.commMsgHdr.type = RAN_L1_API_MSG_PARAMS_RSP;
            paramsRsp.commMsgHdr.size = sizeof(paramsRsp);
            paramsRsp.nbIdx = ((S_RanL1ApiMsgParamsReq *)pCommMsgHdr)->nbIdx;
            paramsRsp.cellIdx = ((S_RanL1ApiMsgParamsReq *)pCommMsgHdr)->cellIdx;
            VRB_PUT(rc, pCellInst->pL1L2CpMsgQue, &paramsRsp, sizeof(paramsRsp));
        }
            break;
        case RAN_L1_API_MSG_CONFIG_REQ:
        {
            CSP_LOG_INF(RAN_MD_L1_COMM, "RAN_L1_CellCpProcess() recv RAN_L1_API_MSG_CONFIG_REQ\n");

            S_RanL1ApiMsgConfigRsp configRsp;
            configRsp.commMsgHdr.type = RAN_L1_API_MSG_CONFIG_RSP;
            configRsp.commMsgHdr.size = sizeof(configRsp);
            configRsp.nbIdx = ((S_RanL1ApiMsgConfigReq *)pCommMsgHdr)->nbIdx;
            configRsp.cellIdx = ((S_RanL1ApiMsgConfigReq *)pCommMsgHdr)->cellIdx;
            VRB_PUT(rc, pCellInst->pL1L2CpMsgQue, &configRsp, sizeof(configRsp));
        }
            break;
        case RAN_L1_API_MSG_START_REQ:
        {
            CSP_LOG_INF(RAN_MD_L1_COMM, "RAN_L1_CellCpProcess() recv RAN_L1_API_MSG_START_REQ\n");

            S_RanL1ApiMsgStartRsp startRsp;
            startRsp.commMsgHdr.type = RAN_L1_API_MSG_START_RSP;
            startRsp.commMsgHdr.size = sizeof(startRsp);
            startRsp.nbIdx = ((S_RanL1ApiMsgStartReq *)pCommMsgHdr)->nbIdx;
            startRsp.cellIdx = ((S_RanL1ApiMsgStartReq *)pCommMsgHdr)->cellIdx;
            VRB_PUT(rc, pCellInst->pL1L2CpMsgQue, &startRsp, sizeof(startRsp));

            pCellInst->appState = APP_STATE_RUNNING;
        }
            break;
        default:
            CSP_LOG_WRN(RAN_MD_L1_COMM, "RAN_L1_CellCpProcess() recv unsupport msg[%#x]\n", pCommMsgHdr->type);
            break;
        }
        VarRB_ReadConsume(pCellInst->pL2L1CpMsgQue, pCommMsgHdr);
    }
}

void RAN_L1_NodeBProcess(S_RanL1NbInst *pNbInst)
{
    uint32_t cellLoop;
    S_RanL1CellInst *pCellInst;

    for (cellLoop = 0; cellLoop < RAN_CELL_NUM_MAX; cellLoop++)
    {
        pCellInst = &pNbInst->cellInst[cellLoop];
        IF_CONTINUE(==, FALSE, pCellInst->inUse, NO_LOG_MESSAGE);
        RAN_L1_CellProcess(pCellInst);
    }
}

void RAN_L1_RanProcess()
{
    uint32_t nbLoop;
    S_RanL1NbInst *pNbInst;

    for (nbLoop = 0; nbLoop < RAN_NODEB_NUM_MAX; nbLoop++)
    {
        pNbInst = &gpRanL1Context->nbInst[nbLoop];
        IF_CONTINUE(==, FALSE, pNbInst->inUse, NO_LOG_MESSAGE);
        RAN_L1_NodeBProcess(pNbInst);
    }
}

void RAN_L1_Process()
{
    RAN_L1_RanProcess();
    RAN_L1_DL_Process();
    usleep(1000);
    RAN_L1_UL_Process();
}

void *RAN_L1_Task(void* arg)
{
    while (1)
    {
        RAN_L1_Process();
        pthread_testcancel();
    }
    return NULL;
}

int32_t RAN_L1_Start(void)
{
    int32_t rc;

    rc = OS_ThreadCreate(&gpRanL1Context->tid, RAN_L1_Task, NULL);
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_L1_Start() OS_ThreadCreate() error!\n");

    CSP_LOG_INF(RAN_MD_L1_COMM, "RAN_L1_Start() success.\n");

    return O5G_RC_OK;
}

int32_t RAN_L1_Stop(void)
{
    int32_t rc;

    rc = OS_ThreadCancel(gpRanL1Context->tid);
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_L1_Stop() OS_ThreadCancel() error!\n");

    CSP_LOG_INF(RAN_MD_L1_COMM, "RAN_L1_Stop() success.\n");

    return O5G_RC_OK;
}
